using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Reflection;

#if !NUNIT
using Microsoft.VisualStudio.TestTools.UnitTesting;
#else
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
#endif

namespace DeflateLib.Tests
{
    [TestClass]
    public class HuffmanTreeTests
    {
        [TestMethod]
        public void TestPackaging()
        {
            int[] counts = {3, 30, 1, 1 };
            int[] resultLength = { 2, 1, 3, 3 };

            int[] length = DeflateUtilsProxy.GetCodeLengths(counts);
            Assert.AreEqual(resultLength, length);
        }

        [TestMethod]
        public void TestPackagingOf1()
        {
            int[] counts = { 2 };
            int[] resultLength = { 1 };

            int[] length = DeflateUtilsProxy.GetCodeLengths(counts);
            Assert.AreEqual(resultLength, length);
        }

        [TestMethod]
        public void TestPackagingMaxLength()
        {
            int[] counts = { 1, 2, 4, 8, 16, 1 };
            int[] resultLength = { 3, 3, 3, 2, 2, 3 };

            int[] length = DeflateUtilsProxy.GetCodeLengths(counts, 3);
            Assert.AreEqual(resultLength, length);
        }

        [TestMethod]
        public void TestBuildCodes()
        {
            int[] lengths = { 2, 1, 3, 3 };

            TreeNodeProxy[] codes = DeflateUtilsProxy.BuildCodes(lengths);
            Assert.AreEqual(lengths.Length, codes.Length);
            Assert.AreEqual(2, codes[0].Code);
            Assert.AreEqual(2, codes[0].Len);
            Assert.AreEqual(0, codes[1].Code);
            Assert.AreEqual(1, codes[1].Len);
            Assert.AreEqual(6, codes[2].Code);
            Assert.AreEqual(3, codes[2].Len);
            Assert.AreEqual(7, codes[3].Code);
            Assert.AreEqual(3, codes[3].Len);
        }

        private class DeflateUtilsProxy
        {
            static Type targetType = Type.GetType("DeflateLib.DeflateUtils,DeflateLib", true);

            internal static int[] GetCodeLengths(int[] counts)
            {
                return (int[])targetType.InvokeMember("GetCodeLengths", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                    null, null, new object[] {counts});
            }

            internal static int[] GetCodeLengths(int[] counts, int maxLength)
            {
                return (int[])targetType.InvokeMember("GetCodeLengths", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                    null, null, new object[] { counts, maxLength });
            }

            internal static TreeNodeProxy[] BuildCodes(int[] lengths)
            {
                Array array = (Array)targetType.InvokeMember("BuildCodes", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
                    null, null, new object[] { lengths });
                TreeNodeProxy[] result = new TreeNodeProxy[array.Length];
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = new TreeNodeProxy(array.GetValue(i));
                }
                return result;
            }

        }

        private class TreeNodeProxy
        {
            static Type targetType = Type.GetType("DeflateLib.TreeNode,DeflateLib", true);
            object target;

            public TreeNodeProxy(object target)
            {
                this.target = target;
            }

            public int Code
            {
                get
                {
                    return (int)targetType.InvokeMember("Code", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField,
                  null, target, null);
                }
            }

            public int Len
            {
                get
                {
                    return (int)targetType.InvokeMember("Len", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField,
                  null, target, null);
                }
            }
        }
    }

}
